using System;
using System.Reflection;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.Serialization;

namespace Crainiate.Data.Persistence
{
    [Serializable]
	public class Results<T>: List<IPersistable>, IResults, ISerializable, IDeserializationCallback
    where T: class, IPersistable
	{
		//Property variables
        private Type _type;
        private Type _activateType;
        private IRelationships _relationships;

        //Working variables
        private SerializationInfo mInfo;

		#region Interface

		//Constructors
		public Results()
		{
            SetTypes();
		}

        public Results(IList<IPersistable> collection): base(collection)
        {
            SetTypes();
        }

        protected Results(SerializationInfo info, StreamingContext context)
        {
            mInfo = info;
            SetTypes();
        }

		//Properties
        public virtual Type Type
        {
            get
            {
                return _type;
            }
        }

        public virtual Type ActivateType
        {
            get
            {
                return _activateType;
            }
        }

        public new T this[int index]
        { 
            get
            {
                return base[index] as T; //use as incase value is null
            }
            set
            {
                base[index] = value;
            }
        }

        public virtual IRelationships Relationships
        {
            get
            {
                return _relationships;
            }
            set
            {
                _relationships = value;
            }
        }

		//Methods
        //Sorts the Entities using the sort provided
        public virtual void Sort(ISort sort)
        {	
            if (sort == null) throw new ArgumentNullException("sort", "Sort may not be null.");
			
            FieldComparer comparer = new FieldComparer(sort);
            base.Sort(comparer);
        }

        //Enumerates the collection using a filter
        public virtual IEnumerable<T> FilterBy(IColumn column, object value)
        {
            if (!column.PropertyInfo.DeclaringType.IsAssignableFrom(typeof(T))) throw new PersistenceException("Column provided is not assignable from Type " + typeof(T).ToString());

            foreach (T item in this)
            {
                object check = column.PropertyInfo.GetValue(item, null);

                if (check == null && value == null) yield return item; 
                if (check != null && check.Equals(value)) yield return item;                    
            }
        }

		#endregion

        #region Implementation

        public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            info.AddValue("Values", ToArray());
            info.AddValue("Relationships", Relationships);
        }
        
        public virtual void OnDeserialization(object sender)
        {
            IPersistable[] array = (IPersistable[]) mInfo.GetValue("Values",typeof(IPersistable[]));

            foreach (IPersistable persist in array)
            {
                Add(persist);
            }

            _relationships = mInfo.GetValue("Relationships", typeof(Relationships)) as Relationships;

            mInfo = null;
        }

        private void SetTypes()
        {
            _activateType = typeof(T);
            _type = _activateType;

            //Check if a view
            if (typeof(IView).IsAssignableFrom(_type))
            {
                //Get the view type
                while (!_type.IsGenericType)
                {
                    _type = _type.BaseType;
                }

                //Get the type used in the generic
                _type = _type.GetGenericArguments()[0];
            }
        }

        #endregion
    }
}
